// 版权所有2009年围棋作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

/*
	Package flag implements command-line flag parsing.

	Usage

	Define flags using flag.String(), Bool(), Int(), etc.

	This declares an integer flag, -n, stored in the pointer nFlag, with type *int:
		import "flag"
		var nFlag = flag.Int("n", 1234, "help message for flag n")
	If you like, you can bind the flag to a variable using the Var() functions.
		var flagvar int
		func init() {
			flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
		}
	Or you can create custom flags that satisfy the Value interface (with
	pointer receivers) and couple them to flag parsing by
		flag.Var(&flagVal, "name", "help message for flagname")
	For such flags, the default value is just the initial value of the variable.

	After all flags are defined, call
		flag.Parse()
	to parse the command line into the defined flags.

	Flags may then be used directly. If you're using the flags themselves,
	they are all pointers; if you bind to variables, they're values.
		fmt.Println("ip has value ", *ip)
		fmt.Println("flagvar has value ", flagvar)

	After parsing, the arguments following the flags are available as the
	slice flag.Args() or individually as flag.Arg(i).
	The arguments are indexed from 0 through flag.NArg()-1.

	Command line flag syntax

	The following forms are permitted:

		-flag
		-flag=x
		-flag x  // 仅限非布尔标志
	One or two minus signs may be used; they are equivalent.
	The last form is not permitted for boolean flags because the
	meaning of the command
		cmd -x *
	where * is a Unix shell wildcard, will change if there is a file
	called 0, false, etc. You must use the -flag=false form to turn
	off a boolean flag.

	Flag parsing stops just before the first non-flag argument
	("-" is a non-flag argument) or after the terminator "--".

	Integer flags accept 1234, 0664, 0x1234 and may be negative.
	Boolean flags may be:
		1, 0, t, f, T, F, true, false, TRUE, FALSE, True, False
	Duration flags accept any input valid for time.ParseDuration.

	The default set of command-line flags is controlled by
	top-level functions.  The FlagSet type allows one to define
	independent sets of flags, such as to implement subcommands
	in a command-line interface. The methods of FlagSet are
	analogous to the top-level functions for the command-line
	flag set.
*/
package flag

import (
	"errors"
	"fmt"
	"io"
	"os"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"time"
)

// ErrHelp是调用-help或-h标志时返回的错误
// 但没有定义这样的标志。
var ErrHelp = errors.New("flag: help requested")

// 如果无法解析标志的值，例如Int的整数无效，则errParse由Set返回。
// 然后通过failf对其进行包装，以提供更多信息。
var errParse = errors.New("parse error")

// 如果标志的值超出范围，则由Set返回errRange。
// 然后通过failf对其进行包装，以提供更多信息。
var errRange = errors.New("value out of range")

func numError(err error) error {
	ne, ok := err.(*strconv.NumError)
	if !ok {
		return err
	}
	if ne.Err == strconv.ErrSyntax {
		return errParse
	}
	if ne.Err == strconv.ErrRange {
		return errRange
	}
	return err
}

// --布尔值
type boolValue bool

func newBoolValue(val bool, p *bool) *boolValue {
	*p = val
	return (*boolValue)(p)
}

func (b *boolValue) Set(s string) error {
	v, err := strconv.ParseBool(s)
	if err != nil {
		err = errParse
	}
	*b = boolValue(v)
	return err
}

func (b *boolValue) Get() interface{} { return bool(*b) }

func (b *boolValue) String() string { return strconv.FormatBool(bool(*b)) }

func (b *boolValue) IsBoolFlag() bool { return true }

// 可选接口，用于指示可以使用的布尔标志
// 提供时没有“=值”文本
type boolFlag interface {
	Value
	IsBoolFlag() bool
}

// --整数值
type intValue int

func newIntValue(val int, p *int) *intValue {
	*p = val
	return (*intValue)(p)
}

func (i *intValue) Set(s string) error {
	v, err := strconv.ParseInt(s, 0, strconv.IntSize)
	if err != nil {
		err = numError(err)
	}
	*i = intValue(v)
	return err
}

func (i *intValue) Get() interface{} { return int(*i) }

func (i *intValue) String() string { return strconv.Itoa(int(*i)) }

// --int64值
type int64Value int64

func newInt64Value(val int64, p *int64) *int64Value {
	*p = val
	return (*int64Value)(p)
}

func (i *int64Value) Set(s string) error {
	v, err := strconv.ParseInt(s, 0, 64)
	if err != nil {
		err = numError(err)
	}
	*i = int64Value(v)
	return err
}

func (i *int64Value) Get() interface{} { return int64(*i) }

func (i *int64Value) String() string { return strconv.FormatInt(int64(*i), 10) }

// --单位价值
type uintValue uint

func newUintValue(val uint, p *uint) *uintValue {
	*p = val
	return (*uintValue)(p)
}

func (i *uintValue) Set(s string) error {
	v, err := strconv.ParseUint(s, 0, strconv.IntSize)
	if err != nil {
		err = numError(err)
	}
	*i = uintValue(v)
	return err
}

func (i *uintValue) Get() interface{} { return uint(*i) }

func (i *uintValue) String() string { return strconv.FormatUint(uint64(*i), 10) }

// --uint64值
type uint64Value uint64

func newUint64Value(val uint64, p *uint64) *uint64Value {
	*p = val
	return (*uint64Value)(p)
}

func (i *uint64Value) Set(s string) error {
	v, err := strconv.ParseUint(s, 0, 64)
	if err != nil {
		err = numError(err)
	}
	*i = uint64Value(v)
	return err
}

func (i *uint64Value) Get() interface{} { return uint64(*i) }

func (i *uint64Value) String() string { return strconv.FormatUint(uint64(*i), 10) }

// --字符串值
type stringValue string

func newStringValue(val string, p *string) *stringValue {
	*p = val
	return (*stringValue)(p)
}

func (s *stringValue) Set(val string) error {
	*s = stringValue(val)
	return nil
}

func (s *stringValue) Get() interface{} { return string(*s) }

func (s *stringValue) String() string { return string(*s) }

// --浮点64值
type float64Value float64

func newFloat64Value(val float64, p *float64) *float64Value {
	*p = val
	return (*float64Value)(p)
}

func (f *float64Value) Set(s string) error {
	v, err := strconv.ParseFloat(s, 64)
	if err != nil {
		err = numError(err)
	}
	*f = float64Value(v)
	return err
}

func (f *float64Value) Get() interface{} { return float64(*f) }

func (f *float64Value) String() string { return strconv.FormatFloat(float64(*f), 'g', -1, 64) }

// --时间。持续时间值
type durationValue time.Duration

func newDurationValue(val time.Duration, p *time.Duration) *durationValue {
	*p = val
	return (*durationValue)(p)
}

func (d *durationValue) Set(s string) error {
	v, err := time.ParseDuration(s)
	if err != nil {
		err = errParse
	}
	*d = durationValue(v)
	return err
}

func (d *durationValue) Get() interface{} { return time.Duration(*d) }

func (d *durationValue) String() string { return (*time.Duration)(d).String() }

type funcValue func(string) error

func (f funcValue) Set(s string) error { return f(s) }

func (f funcValue) String() string { return "" }

// Value是存储在标志中的动态值的接口。
// （默认值表示为字符串。）
// None
// 如果某个值的IsBoolFlag（）bool方法返回true，
// 命令行解析器使-name等效于-name=true
// 而不是使用下一个命令行参数。
// None
// 对于存在的每个标志，按命令行顺序调用Set一次。
// flag包可以使用零值接收器调用String方法，
// 例如零指针。
type Value interface {
	String() string
	Set(string) error
}

// Getter是一个允许检索值内容的接口。
// 它包装了值接口，而不是它的一部分，因为
// 出现在Go 1及其兼容规则之后。提供的所有值类型
// 除Func使用的类型外，此包满足Getter接口。
type Getter interface {
	Value
	Get() interface{}
}

// ErrorHandling定义解析失败时FlagSet.Parse的行为。
type ErrorHandling int

// 如果解析失败，这些常量将导致FlagSet.Parse按所述方式运行。
const (
	ContinueOnError ErrorHandling = iota // 返回一个描述性错误。
	ExitOnError                          // 调用os.Exit（2）或-h/-help退出（0）。
	PanicOnError                         // 调用带有描述错误的panic。
)

// 标志集表示一组已定义的标志。标志集的零值
// 没有名称，并且具有错误处理。
// None
// 标志名称在标志集中必须是唯一的。试图定义其
// 名称已在使用中，将导致恐慌。
type FlagSet struct {
	// Usage是在解析标志时发生错误时调用的函数。
	// 该字段是可以更改为指向的函数（不是方法）
	// 自定义错误处理程序。调用Usage之后会发生什么取决于
	// 关于错误处理设置；对于命令行，这是默认值
	// 到ExitOnError，它在调用Usage后退出程序。
	Usage func()

	name          string
	parsed        bool
	actual        map[string]*Flag
	formal        map[string]*Flag
	args          []string // 标志后的参数
	errorHandling ErrorHandling
	output        io.Writer // nil表示标准差；使用Output（）访问器
}

// 国旗代表国旗的状态。
type Flag struct {
	Name     string // 在命令行上显示的名称
	Usage    string // 帮助信息
	Value    Value  // 设定值
	DefValue string // 默认值（作为文本）；查看使用信息
}

// sortFlags以字典排序顺序作为片段返回标志。
func sortFlags(flags map[string]*Flag) []*Flag {
	result := make([]*Flag, len(flags))
	i := 0
	for _, f := range flags {
		result[i] = f
		i++
	}
	sort.Slice(result, func(i, j int) bool {
		return result[i].Name < result[j].Name
	})
	return result
}

// 输出返回用法和错误消息的目标。如果
// 输出未设置或设置为零。
func (f *FlagSet) Output() io.Writer {
	if f.output == nil {
		return os.Stderr
	}
	return f.output
}

// Name返回标志集的名称。
func (f *FlagSet) Name() string {
	return f.name
}

// ErrorHandling返回标志集的错误处理行为。
func (f *FlagSet) ErrorHandling() ErrorHandling {
	return f.errorHandling
}

// SetOutput设置使用和错误消息的目标。
// 如果输出为nil，则使用os.Stderr。
func (f *FlagSet) SetOutput(output io.Writer) {
	f.output = output
}

// VisitAll按字典顺序访问旗帜，并为每个旗帜调用fn。
// 它访问所有的旗帜，甚至那些没有设置。
func (f *FlagSet) VisitAll(fn func(*Flag)) {
	for _, flag := range sortFlags(f.formal) {
		fn(flag)
	}
}

// VisitAll按字典顺序访问命令行标志，调用
// 每一个都是fn。它访问所有的旗帜，甚至那些没有设置。
func VisitAll(fn func(*Flag)) {
	CommandLine.VisitAll(fn)
}

// Visit按字典顺序访问旗帜，并为每个旗帜调用fn。
// 它只访问那些已设置的标志。
func (f *FlagSet) Visit(fn func(*Flag)) {
	for _, flag := range sortFlags(f.actual) {
		fn(flag)
	}
}

// Visit按字典顺序访问命令行标志，调用fn
// 每个人。它只访问那些已设置的标志。
func Visit(fn func(*Flag)) {
	CommandLine.Visit(fn)
}

// 查找返回命名标志的标志结构，如果不存在，则返回nil。
func (f *FlagSet) Lookup(name string) *Flag {
	return f.formal[name]
}

// Lookup返回命名命令行标志的标志结构，
// 如果不存在，则返回nil。
func Lookup(name string) *Flag {
	return CommandLine.formal[name]
}

// Set设置命名标志的值。
func (f *FlagSet) Set(name, value string) error {
	flag, ok := f.formal[name]
	if !ok {
		return fmt.Errorf("no such flag -%v", name)
	}
	err := flag.Value.Set(value)
	if err != nil {
		return err
	}
	if f.actual == nil {
		f.actual = make(map[string]*Flag)
	}
	f.actual[name] = flag
	return nil
}

// Set设置命名命令行标志的值。
func Set(name, value string) error {
	return CommandLine.Set(name, value)
}

// isZeroValue确定字符串是否表示零
// 标志的值。
func isZeroValue(flag *Flag, value string) bool {
	// 构建标志值类型的零值，并查看
	// 调用其String方法的结果等于传入的值。
	// 除非值类型本身是接口类型，否则此操作有效。
	typ := reflect.TypeOf(flag.Value)
	var z reflect.Value
	if typ.Kind() == reflect.Ptr {
		z = reflect.New(typ.Elem())
	} else {
		z = reflect.Zero(typ)
	}
	return value == z.Interface().(Value).String()
}

// UnquoteUsage从用法中提取一个后引号的名称
// 字符串，并返回它和未引用的用法。
// 给定“a`name`to show”，它返回（“name”，“a name to show”）。
// 如果没有反引号，则名称是对
// 标志值的类型，如果标志为布尔值，则为空字符串。
func UnquoteUsage(flag *Flag) (name string, usage string) {
	// 查找后引号的名称，但避免使用strings包。
	usage = flag.Usage
	for i := 0; i < len(usage); i++ {
		if usage[i] == '`' {
			for j := i + 1; j < len(usage); j++ {
				if usage[j] == '`' {
					name = usage[i+1 : j]
					usage = usage[:i] + name + usage[j+1:]
					return name, usage
				}
			}
			break // 只有一个回报价；使用类型名称。
		}
	}
	// 没有显式名称，所以如果可以找到，请使用type。
	name = "value"
	switch flag.Value.(type) {
	case boolFlag:
		name = ""
	case *durationValue:
		name = "duration"
	case *float64Value:
		name = "float"
	case *intValue, *int64Value:
		name = "int"
	case *stringValue:
		name = "string"
	case *uintValue, *uint64Value:
		name = "uint"
	}
	return
}

// PrintDefaults打印为标准错误，除非另有配置
// 集合中所有已定义命令行标志的默认值。见
// 有关详细信息，请参阅全局函数PrintDefaults的文档。
func (f *FlagSet) PrintDefaults() {
	f.VisitAll(func(flag *Flag) {
		var b strings.Builder
		fmt.Fprintf(&b, "  -%s", flag.Name) // -前两个空格；请参阅下面的两条评论。
		name, usage := UnquoteUsage(flag)
		if len(name) > 0 {
			b.WriteString(" ")
			b.WriteString(name)
		}
		// 一个ASCII字母的布尔标志非常常见，我们
		// 特别对待他们，把他们的用法放在同一条线上。
		if b.Len() <= 4 { // 空间，空间，'-'，x'。
			b.WriteString("\t")
		} else {
			// 制表符前的四个空格触发良好对齐
			// 对于4空间和8空间制表位。
			b.WriteString("\n    \t")
		}
		b.WriteString(strings.ReplaceAll(usage, "\n", "\n    \t"))

		if !isZeroValue(flag, flag.DefValue) {
			if _, ok := flag.Value.(*stringValue); ok {
				// 在值上加引号
				fmt.Fprintf(&b, " (default %q)", flag.DefValue)
			} else {
				fmt.Fprintf(&b, " (default %v)", flag.DefValue)
			}
		}
		fmt.Fprint(f.Output(), b.String(), "\n")
	})
}

// 打印默认值打印为标准错误，除非另有配置，
// 显示所有已定义组件的默认设置的使用信息
// 命令行标志。
// 对于整数值标志x，默认输出的形式为
// -x int
// 用法-message-for-x（默认值7）
// 用法消息将显示在单独的一行中，但
// 具有单字节名称的布尔标志。对于bool标志，类型为
// 省略，如果标志名称为一个字节，则会显示用法消息
// 在同一条线上。如果
// 默认值是该类型的零值。列出的类型，这里是int，
// 可以通过在标志的用法中放置回引名称来更改
// 一串消息中的第一个此类项被视为参数
// 要在消息中显示的名称和后引号将从
// 显示时显示消息。例如，给定
// flag.String（“I”，“”，“搜索包含文件的`目录'”）
// 输出将是
// -I目录
// 搜索包含文件的目录。
// None
// 要更改标志消息的目标，请调用CommandLine.SetOutput。
func PrintDefaults() {
	CommandLine.PrintDefaults()
}

// defaultUsage是打印使用情况消息的默认函数。
func (f *FlagSet) defaultUsage() {
	if f.name == "" {
		fmt.Fprintf(f.Output(), "Usage:\n")
	} else {
		fmt.Fprintf(f.Output(), "Usage of %s:\n", f.name)
	}
	f.PrintDefaults()
}

// 注意：用法不仅仅是默认用法（命令行）
// 因为它（通过使用godoc标志）作为示例
// 了解如何编写自己的使用函数。

// 用法打印一条用法消息，记录所有定义的命令行标志
// 到命令行的输出，默认情况下为os.Stderr。
// 在分析标志时发生错误时调用它。
// 函数是一个变量，可以更改为指向自定义函数。
// 默认情况下，它打印一个简单的标题并调用PrintDefaults；有关
// 输出的格式以及如何控制，请参阅文档了解PrintDefault。
// 自定义使用功能可以选择退出程序；默认退出
// 当命令行的错误处理策略设置为
// ExitOnError。
var Usage = func() {
	fmt.Fprintf(CommandLine.Output(), "Usage of %s:\n", os.Args[0])
	PrintDefaults()
}

// NFlag返回已设置的标志数。
func (f *FlagSet) NFlag() int { return len(f.actual) }

// NFlag返回已设置的命令行标志数。
func NFlag() int { return len(CommandLine.actual) }

// Arg返回第i个参数。Arg（0）是剩余的第一个参数
// 在处理标志之后。如果
// 请求的元素不存在。
func (f *FlagSet) Arg(i int) string {
	if i < 0 || i >= len(f.args) {
		return ""
	}
	return f.args[i]
}

// Arg返回第i个命令行参数。Arg（0）是剩余的第一个参数
// 在处理标志之后。如果
// 请求的元素不存在。
func Arg(i int) string {
	return CommandLine.Arg(i)
}

// NArg是处理标志后剩余的参数数。
func (f *FlagSet) NArg() int { return len(f.args) }

// NArg是处理标志后剩余的参数数。
func NArg() int { return len(CommandLine.args) }

// Args返回非标志参数。
func (f *FlagSet) Args() []string { return f.args }

// Args返回非标志命令行参数。
func Args() []string { return CommandLine.args }

// BoolVar使用指定的名称、默认值和用法字符串定义bool标志。
// 参数p指向存储标志值的bool变量。
func (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string) {
	f.Var(newBoolValue(value, p), name, usage)
}

// BoolVar使用指定的名称、默认值和用法字符串定义bool标志。
// 参数p指向存储标志值的bool变量。
func BoolVar(p *bool, name string, value bool, usage string) {
	CommandLine.Var(newBoolValue(value, p), name, usage)
}

// Bool使用指定的名称、默认值和用法字符串定义Bool标志。
// 返回值是存储标志值的bool变量的地址。
func (f *FlagSet) Bool(name string, value bool, usage string) *bool {
	p := new(bool)
	f.BoolVar(p, name, value, usage)
	return p
}

// Bool使用指定的名称、默认值和用法字符串定义Bool标志。
// 返回值是存储标志值的bool变量的地址。
func Bool(name string, value bool, usage string) *bool {
	return CommandLine.Bool(name, value, usage)
}

// IntVar使用指定的名称、默认值和用法字符串定义一个int标志。
// 参数p指向一个int变量，在该变量中存储标志的值。
func (f *FlagSet) IntVar(p *int, name string, value int, usage string) {
	f.Var(newIntValue(value, p), name, usage)
}

// IntVar使用指定的名称、默认值和用法字符串定义一个int标志。
// 参数p指向一个int变量，在该变量中存储标志的值。
func IntVar(p *int, name string, value int, usage string) {
	CommandLine.Var(newIntValue(value, p), name, usage)
}

// Int使用指定的名称、默认值和用法字符串定义Int标志。
// 返回值是存储标志值的int变量的地址。
func (f *FlagSet) Int(name string, value int, usage string) *int {
	p := new(int)
	f.IntVar(p, name, value, usage)
	return p
}

// Int使用指定的名称、默认值和用法字符串定义Int标志。
// 返回值是存储标志值的int变量的地址。
func Int(name string, value int, usage string) *int {
	return CommandLine.Int(name, value, usage)
}

// Int64Var使用指定的名称、默认值和用法字符串定义int64标志。
// 参数p指向一个int64变量，用于存储标志值。
func (f *FlagSet) Int64Var(p *int64, name string, value int64, usage string) {
	f.Var(newInt64Value(value, p), name, usage)
}

// Int64Var使用指定的名称、默认值和用法字符串定义int64标志。
// 参数p指向一个int64变量，用于存储标志值。
func Int64Var(p *int64, name string, value int64, usage string) {
	CommandLine.Var(newInt64Value(value, p), name, usage)
}

// Int64使用指定的名称、默认值和用法字符串定义Int64标志。
// 返回值是存储标志值的int64变量的地址。
func (f *FlagSet) Int64(name string, value int64, usage string) *int64 {
	p := new(int64)
	f.Int64Var(p, name, value, usage)
	return p
}

// Int64使用指定的名称、默认值和用法字符串定义Int64标志。
// 返回值是存储标志值的int64变量的地址。
func Int64(name string, value int64, usage string) *int64 {
	return CommandLine.Int64(name, value, usage)
}

// UintVar使用指定的名称、默认值和用法字符串定义uint标志。
// 参数p指向一个uint变量，该变量用于存储标志的值。
func (f *FlagSet) UintVar(p *uint, name string, value uint, usage string) {
	f.Var(newUintValue(value, p), name, usage)
}

// UintVar使用指定的名称、默认值和用法字符串定义uint标志。
// 参数p指向一个uint变量，该变量用于存储标志的值。
func UintVar(p *uint, name string, value uint, usage string) {
	CommandLine.Var(newUintValue(value, p), name, usage)
}

// Uint使用指定的名称、默认值和用法字符串定义Uint标志。
// 返回值是存储标志值的uint变量的地址。
func (f *FlagSet) Uint(name string, value uint, usage string) *uint {
	p := new(uint)
	f.UintVar(p, name, value, usage)
	return p
}

// Uint使用指定的名称、默认值和用法字符串定义Uint标志。
// 返回值是存储标志值的uint变量的地址。
func Uint(name string, value uint, usage string) *uint {
	return CommandLine.Uint(name, value, usage)
}

// Uint64Var使用指定的名称、默认值和用法字符串定义uint64标志。
// 参数p指向一个uint64变量，该变量用于存储标志值。
func (f *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string) {
	f.Var(newUint64Value(value, p), name, usage)
}

// Uint64Var使用指定的名称、默认值和用法字符串定义uint64标志。
// 参数p指向一个uint64变量，该变量用于存储标志值。
func Uint64Var(p *uint64, name string, value uint64, usage string) {
	CommandLine.Var(newUint64Value(value, p), name, usage)
}

// Uint64使用指定的名称、默认值和用法字符串定义Uint64标志。
// 返回值是存储标志值的uint64变量的地址。
func (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64 {
	p := new(uint64)
	f.Uint64Var(p, name, value, usage)
	return p
}

// Uint64使用指定的名称、默认值和用法字符串定义Uint64标志。
// 返回值是存储标志值的uint64变量的地址。
func Uint64(name string, value uint64, usage string) *uint64 {
	return CommandLine.Uint64(name, value, usage)
}

// StringVar使用指定的名称、默认值和用法字符串定义字符串标志。
// 参数p指向存储标志值的字符串变量。
func (f *FlagSet) StringVar(p *string, name string, value string, usage string) {
	f.Var(newStringValue(value, p), name, usage)
}

// StringVar使用指定的名称、默认值和用法字符串定义字符串标志。
// 参数p指向存储标志值的字符串变量。
func StringVar(p *string, name string, value string, usage string) {
	CommandLine.Var(newStringValue(value, p), name, usage)
}

// String使用指定的名称、默认值和用法字符串定义字符串标志。
// 返回值是存储标志值的字符串变量的地址。
func (f *FlagSet) String(name string, value string, usage string) *string {
	p := new(string)
	f.StringVar(p, name, value, usage)
	return p
}

// String使用指定的名称、默认值和用法字符串定义字符串标志。
// 返回值是存储标志值的字符串变量的地址。
func String(name string, value string, usage string) *string {
	return CommandLine.String(name, value, usage)
}

// Float64Var使用指定的名称、默认值和用法字符串定义float64标志。
// 参数p指向一个float64变量，用于存储标志值。
func (f *FlagSet) Float64Var(p *float64, name string, value float64, usage string) {
	f.Var(newFloat64Value(value, p), name, usage)
}

// Float64Var使用指定的名称、默认值和用法字符串定义float64标志。
// 参数p指向一个float64变量，用于存储标志值。
func Float64Var(p *float64, name string, value float64, usage string) {
	CommandLine.Var(newFloat64Value(value, p), name, usage)
}

// Float64使用指定的名称、默认值和用法字符串定义Float64标志。
// 返回值是存储标志值的float64变量的地址。
func (f *FlagSet) Float64(name string, value float64, usage string) *float64 {
	p := new(float64)
	f.Float64Var(p, name, value, usage)
	return p
}

// Float64使用指定的名称、默认值和用法字符串定义Float64标志。
// 返回值是存储标志值的float64变量的地址。
func Float64(name string, value float64, usage string) *float64 {
	return CommandLine.Float64(name, value, usage)
}

// DurationVar使用指定的名称、默认值和用法字符串定义time.Duration标志。
// 参数p指向一个time.Duration变量，用于存储标志值。
// 该标志接受time.ParseDuration可接受的值。
func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
	f.Var(newDurationValue(value, p), name, usage)
}

// DurationVar使用指定的名称、默认值和用法字符串定义time.Duration标志。
// 参数p指向一个time.Duration变量，用于存储标志值。
// 该标志接受time.ParseDuration可接受的值。
func DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
	CommandLine.Var(newDurationValue(value, p), name, usage)
}

// Duration使用指定的名称、默认值和用法字符串定义time.Duration标志。
// 返回值是存储标志值的time.Duration变量的地址。
// 该标志接受time.ParseDuration可接受的值。
func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration {
	p := new(time.Duration)
	f.DurationVar(p, name, value, usage)
	return p
}

// Duration使用指定的名称、默认值和用法字符串定义time.Duration标志。
// 返回值是存储标志值的time.Duration变量的地址。
// 该标志接受time.ParseDuration可接受的值。
func Duration(name string, value time.Duration, usage string) *time.Duration {
	return CommandLine.Duration(name, value, usage)
}

// Func使用指定的名称和用法字符串定义标志。
// 每次看到该标志时，都会使用该标志的值调用fn。
// 如果fn返回非nil错误，则将其视为标志值解析错误。
func (f *FlagSet) Func(name, usage string, fn func(string) error) {
	f.Var(funcValue(fn), name, usage)
}

// Func使用指定的名称和用法字符串定义标志。
// 每次看到该标志时，都会使用该标志的值调用fn。
// 如果fn返回非nil错误，则将其视为标志值解析错误。
func Func(name, usage string, fn func(string) error) {
	CommandLine.Func(name, usage, fn)
}

// Var使用指定的名称和用法字符串定义标志。类型和
// 标志的值由类型为value的第一个参数表示，该参数
// 通常包含用户定义的值实现。例如
// 调用方可以创建一个标志，将逗号分隔的字符串转换为一个片段
// 通过向切片提供值的方法来定义字符串；特别是，Set将
// 将逗号分隔的字符串分解为切片。
func (f *FlagSet) Var(value Value, name string, usage string) {
	// 标志不能以“-”开头或包含“=”。
	if strings.HasPrefix(name, "-") {
		panic(f.sprintf("flag %q begins with -", name))
	} else if strings.Contains(name, "=") {
		panic(f.sprintf("flag %q contains =", name))
	}

	// 记住默认值作为字符串；它不会改变。
	flag := &Flag{name, usage, value, value.String()}
	_, alreadythere := f.formal[name]
	if alreadythere {
		var msg string
		if f.name == "" {
			msg = f.sprintf("flag redefined: %s", name)
		} else {
			msg = f.sprintf("%s flag redefined: %s", f.name, name)
		}
		panic(msg) // 仅当使用相同名称声明标志时发生
	}
	if f.formal == nil {
		f.formal = make(map[string]*Flag)
	}
	f.formal[name] = flag
}

// Var使用指定的名称和用法字符串定义标志。类型和
// 标志的值由类型为value的第一个参数表示，该参数
// 通常包含用户定义的值实现。例如
// 调用方可以创建一个标志，将逗号分隔的字符串转换为一个片段
// 通过向切片提供值的方法来定义字符串；特别是，Set将
// 将逗号分隔的字符串分解为切片。
func Var(value Value, name string, usage string) {
	CommandLine.Var(value, name, usage)
}

// sprintf格式化消息，将其打印到输出，然后返回。
func (f *FlagSet) sprintf(format string, a ...interface{}) string {
	msg := fmt.Sprintf(format, a...)
	fmt.Fprintln(f.Output(), msg)
	return msg
}

// failf将格式化错误和用法消息打印到标准错误，并
// 返回错误。
func (f *FlagSet) failf(format string, a ...interface{}) error {
	msg := f.sprintf(format, a...)
	f.usage()
	return errors.New(msg)
}

// usage调用标志集的usage方法（如果已指定），
// 或相应的默认用法函数。
func (f *FlagSet) usage() {
	if f.Usage == nil {
		f.defaultUsage()
	} else {
		f.Usage()
	}
}

// parseOne解析一个标志。它报告是否看到旗帜。
func (f *FlagSet) parseOne() (bool, error) {
	if len(f.args) == 0 {
		return false, nil
	}
	s := f.args[0]
	if len(s) < 2 || s[0] != '-' {
		return false, nil
	}
	numMinuses := 1
	if s[1] == '-' {
		numMinuses++
		if len(s) == 2 { // “--”终止标志
			f.args = f.args[1:]
			return false, nil
		}
	}
	name := s[numMinuses:]
	if len(name) == 0 || name[0] == '-' || name[0] == '=' {
		return false, f.failf("bad flag syntax: %s", s)
	}

	// 这是一面旗帜。有争论吗？
	f.args = f.args[1:]
	hasValue := false
	value := ""
	for i := 1; i < len(name); i++ { // 平等不能是第一位的
		if name[i] == '=' {
			value = name[i+1:]
			hasValue = true
			name = name[0:i]
			break
		}
	}
	m := f.formal
	flag, alreadythere := m[name] // 缺陷
	if !alreadythere {
		if name == "help" || name == "h" { // 好的帮助信息的特殊情况。
			f.usage()
			return false, ErrHelp
		}
		return false, f.failf("flag provided but not defined: -%s", name)
	}

	if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // 特殊情况：不需要arg
		if hasValue {
			if err := fv.Set(value); err != nil {
				return false, f.failf("invalid boolean value %q for -%s: %v", value, name, err)
			}
		} else {
			if err := fv.Set("true"); err != nil {
				return false, f.failf("invalid boolean flag %s: %v", name, err)
			}
		}
	} else {
		// 它必须有一个值，这可能是下一个参数。
		if !hasValue && len(f.args) > 0 {
			// 值是下一个参数
			hasValue = true
			value, f.args = f.args[0], f.args[1:]
		}
		if !hasValue {
			return false, f.failf("flag needs an argument: -%s", name)
		}
		if err := flag.Value.Set(value); err != nil {
			return false, f.failf("invalid value %q for flag -%s: %v", value, name, err)
		}
	}
	if f.actual == nil {
		f.actual = make(map[string]*Flag)
	}
	f.actual[name] = flag
	return true, nil
}

// Parse解析参数列表中的标志定义，参数列表不应
// 包括命令名。必须在标志集中的所有标志之后调用
// 在程序访问标志之前定义。
// 如果设置了-help或-h但未定义，则返回值将为ErrHelp。
func (f *FlagSet) Parse(arguments []string) error {
	f.parsed = true
	f.args = arguments
	for {
		seen, err := f.parseOne()
		if seen {
			continue
		}
		if err == nil {
			break
		}
		switch f.errorHandling {
		case ContinueOnError:
			return err
		case ExitOnError:
			if err == ErrHelp {
				os.Exit(0)
			}
			os.Exit(2)
		case PanicOnError:
			panic(err)
		}
	}
	return nil
}

// Parsed报告是否调用了f.Parse。
func (f *FlagSet) Parsed() bool {
	return f.parsed
}

// Parse从os.Args[1:]解析命令行标志。必须打电话
// 在定义所有标志之后和程序访问标志之前。
func Parse() {
	// 忽略错误；已为ExitOnError设置命令行。
	CommandLine.Parse(os.Args[1:])
}

// 已解析报告是否已解析命令行标志。
func Parsed() bool {
	return CommandLine.Parsed()
}

// CommandLine是从os.Args解析的默认命令行标志集。
// 顶级函数（如BoolVar、Arg等）是
// 命令行的方法。
var CommandLine = NewFlagSet(os.Args[0], ExitOnError)

func init() {
	// 通过调用全局用法覆盖通用标志集默认用法。
	// 注意：这不是命令行。用法=用法，
	// 因为我们希望任何最终调用使用任何更新的使用值，
	// 不是该行运行时的值。
	CommandLine.Usage = commandLineUsage
}

func commandLineUsage() {
	Usage()
}

// NewFlagSet返回一个新的空标志集，该标志集具有指定的名称和名称
// 错误处理属性。如果名称不为空，将打印该名称
// 在默认用法消息和错误消息中。
func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
	f := &FlagSet{
		name:          name,
		errorHandling: errorHandling,
	}
	f.Usage = f.defaultUsage
	return f
}

// Init为标志集设置名称和错误处理属性。
// 默认情况下，zero标志集使用空名称和
// ContinueOnError错误处理策略。
func (f *FlagSet) Init(name string, errorHandling ErrorHandling) {
	f.name = name
	f.errorHandling = errorHandling
}
